#include "cvxDeckLinkInputCallback.h"
#include "MechAlignDlg.h"

CvxDecklinkInputCallback::CvxDecklinkInputCallback()
{
	m_leftImage = NULL;
	m_rightImage = NULL;
	m_refCount = 1;
	m_size.width = 0;
	m_size.height = 0;
	m_binocular = false;
	m_pDialog = NULL;
	m_frameNum = 0;
	m_capRatio = 30;
}

CvxDecklinkInputCallback::~CvxDecklinkInputCallback()
{
//	ClearImages();	
}



void CvxDecklinkInputCallback::SetResoltion(int w, int h, bool bi)
{
	if (m_size.width == w && m_size.height == h && m_binocular == bi )
	{
		return;
	}
	else
	{
		if (m_leftImage)
		{
			cvReleaseImage(&m_leftImage);
		}
		if (m_rightImage)
		{
			cvReleaseImage(&m_rightImage);
		}
		m_size = cvSize(w, h);
		m_binocular = bi;		
		m_leftImage = cvCreateImage(m_size, IPL_DEPTH_8U, 2);
		m_rightImage = cvCreateImage(m_size, IPL_DEPTH_8U, 2);
		assert(m_leftImage);
		assert(m_rightImage);
	}
}

void CvxDecklinkInputCallback::SetFps(float fps)
{
	if (fps >= 0.4 && fps<= 30)
	{
		m_capRatio = (int)(30.0/fps);
		if (m_capRatio < 1)
		{
			m_capRatio = 1;
		}		
	}
}


HRESULT	STDMETHODCALLTYPE CvxDecklinkInputCallback::QueryInterface(REFIID iid, LPVOID *ppv)
{
	HRESULT			result = E_NOINTERFACE;

	// Initialise the return result
	*ppv = NULL;

	// Obtain the IUnknown interface and compare it the provided REFIID
	if (iid == IID_IUnknown)
	{
		*ppv = this;
		AddRef();
		result = S_OK;
	}
	else if (iid == IID_IDeckLinkInputCallback)
	{
		*ppv = (IDeckLinkInputCallback*)this;
		AddRef();
		result = S_OK;
	}

	return result;
}

ULONG STDMETHODCALLTYPE CvxDecklinkInputCallback::AddRef(void)
{
	return InterlockedIncrement((LONG*)&m_refCount);
}

ULONG STDMETHODCALLTYPE CvxDecklinkInputCallback::Release(void)
{
	int		newRefValue;

	newRefValue = InterlockedDecrement((LONG*)&m_refCount);
	if (newRefValue == 0)
	{
		delete this;
		return 0;
	}

	return newRefValue;
}

HRESULT STDMETHODCALLTYPE CvxDecklinkInputCallback::VideoInputFormatChanged(BMDVideoInputFormatChangedEvents notificationEvents, IDeckLinkDisplayMode* newDisplayMode, BMDDetectedVideoInputFormatFlags detectedSignalFlags)
{
	return S_OK;
}


HRESULT STDMETHODCALLTYPE CvxDecklinkInputCallback::VideoInputFrameArrived(IDeckLinkVideoInputFrame* pArrivedFrame, 
																		   IDeckLinkAudioInputPacket*)
{	
	m_frameNum++;
	if (m_frameNum %m_capRatio != 0)		    
	{
		if (m_frameNum == INT_MAX)
		{
			m_frameNum = 0;
		}
		return S_OK;
	}
	//check size
	int w = pArrivedFrame->GetWidth();
	int h = pArrivedFrame->GetHeight();
	if (w != m_size.width || h != m_size.height)
	{
		AfxMessageBox(_T("frame size not equal."), MB_OK);
		return S_FALSE;
	}
	
	unsigned char *pLeftFrameData = NULL;
	HRESULT rst = pArrivedFrame->GetBytes((void**)&pLeftFrameData);
	assert(rst == S_OK);

	assert(m_leftImage && m_rightImage);	

	//check formate
	BMDPixelFormat pFormart = pArrivedFrame->GetPixelFormat();
	assert(pFormart == bmdFormat8BitYUV);
	
	//record gray image	
	if (pFormart == bmdFormat8BitYUV)
	{
		//get left image data
		memcpy((void*)m_leftImage->imageData, (void*)pLeftFrameData, w * h * 2);

		if (m_binocular) //m_binocular
		{
			IDeckLinkVideoFrame3DExtensions *p3DEctension = NULL;
			rst = pArrivedFrame->QueryInterface(IID_IDeckLinkVideoFrame3DExtensions, 
								(void**)&p3DEctension);
			if (rst != S_OK)
			{
				AfxMessageBox(_T("can not get IDeckLinkVideoFrame3DExtensions!\n"), MB_OK);
				return S_FALSE;
			}
			IDeckLinkVideoFrame *pRightEyeFrame;
			rst = p3DEctension->GetFrameForRightEye(&pRightEyeFrame);
			if (rst != S_OK)
			{
				AfxMessageBox(_T("can not get GetFrameForRightEye!\n"), MB_OK);
				return S_FALSE;
			}
			unsigned char *pRightFrameData = NULL;
			rst = pRightEyeFrame->GetBytes((void**)&pRightFrameData);
			if (rst != S_OK)
			{
				AfxMessageBox(_T("can not get GetFrameForRightEye!\n"), MB_OK);
				return S_FALSE;
			}

			memcpy((void*)m_rightImage->imageData, (void*)pRightFrameData, w * h * 2);

			if (m_rightFlip == 0 || m_rightFlip == 1)
			{
				cvFlip(m_rightImage, m_rightImage, m_rightFlip);
			}
			if (pRightEyeFrame)
			{
				pRightEyeFrame->Release();
			}
			if (p3DEctension)
			{
				p3DEctension->Release();
			}		
		}		
	}	
	else
	{
		AfxMessageBox(_T("pixel format not support."), MB_OK);
		return S_FALSE;
	}

	if (m_pDialog)
	{
		if (m_binocular)
		{
			m_pDialog->updateImages(m_leftImage->imageData, m_rightImage->imageData, w, h, 2, m_rightFlip == 1);
		}
		else
		{
			m_pDialog->updateImages(m_leftImage->imageData, NULL, w, h, 2, false);
		}		
		
	}
		
	return S_OK;
}

void cvxYUV422toGray(int w, int h, unsigned char *pData, IplImage *grayImage)
{
	assert(pData);
	assert(grayImage);
	assert(w%2 == 0);
	IplImage *yuvImage = cvCreateImageHeader(cvSize(w, h), IPL_DEPTH_8U, 2);
	yuvImage->imageData = (char *)pData;
	cvSplit(yuvImage, NULL, grayImage, NULL, NULL);
	cvReleaseImageHeader(&yuvImage);
}
void cvxYUV422toRGB(int w, int h, unsigned char *pData, IplImage *rgbImage, bool flipUV)
{
	assert(pData);
	assert(rgbImage);

	//YUV422 -> YUV
	cvx::YuvImage yuv_img(rgbImage);
	for (int y = 0; y<h; ++y)
	{
		for (int x = 0; x<w; x+=2)
		{
			unsigned char *pD  = pData + 2 * y * w + 2 * x;
			unsigned char *pD2 = pD + 2;
			int u, y1, v, y2;
			if (flipUV)
			{
				v = pD[0];
				y1 = pD[1];
				u = pD2[0];
				y2 = pD2[1];
			}
			else
			{
				u = pD[0];
				y1 = pD[1];
				v = pD2[0];
				y2 = pD2[1];	
			}	
					
			yuv_img[y][x].y = y1;
			yuv_img[y][x].u = u;
			yuv_img[y][x].v = v;
			yuv_img[y][x+1].y = y2;
			yuv_img[y][x+1].u = u;
			yuv_img[y][x+1].v = v;

		}
	}
	//YUV -> BGR
	cvCvtColor(rgbImage, rgbImage, CV_YUV2BGR);
}

